#include "http_request.h"


const std::unordered_set<std::string> HttpRequest::DEFAULT_PAGES = {
        "/index", "/register", "/login",
        "/welcome", "/video", "/picture"
};

HttpRequest::HttpRequest() {
    m_read_idx = m_checked_idx = m_line_idx = 0;
    m_parse_state = PARSE_REQUEST_LINE;
    m_method = m_url = m_path = m_version = m_body = "";
    m_header_tags.clear();
    m_body_kvs.clear();
}

HttpRequest::~HttpRequest() {
    delete [] m_read_buffer;
}

void HttpRequest::init_read_buffer(uint64_t read_buf_size){
    m_read_buf_size = read_buf_size;
    m_read_buffer = new char[read_buf_size]{0};
    m_read_idx = 0;
}

void HttpRequest::reset() {
    m_read_idx = m_checked_idx = m_line_idx = 0;
    m_parse_state = PARSE_REQUEST_LINE;
    m_method = m_url = m_path = m_version = m_body = "";
    m_header_tags.clear();
    m_body_kvs.clear();
}

HttpRequest::LINE_STATUS HttpRequest::parse_line() {
    // 以"/r/n"为完整的行结尾
    for (; m_checked_idx < m_read_idx; ++m_checked_idx) {
        if (m_read_buffer[m_checked_idx] == '\r') {
            if (m_checked_idx + 1 == m_read_idx) {
                return LINE_OPEN;
            } else if (m_read_buffer[m_checked_idx + 1] == '\n'){
                m_read_buffer[m_checked_idx++] = '\0';
                m_read_buffer[m_checked_idx++] = '\0';
                return LINE_OK;
            } else {
                LOG_DEBUG("LINE_BAD")
                return LINE_BAD;
            }
        }
        else if (m_read_buffer[m_checked_idx] == '\n') {
            // 上次读的最后字符为'\r'的情形
            if (m_checked_idx > 0 && m_read_buffer[m_checked_idx] == '\r') {
                m_read_buffer[m_checked_idx - 1] = '\0';
                m_read_buffer[m_checked_idx++] = '\0';
                return LINE_OK;
            } else {
                return LINE_BAD;
            }
        }
    }

    if (m_parse_state == PARSE_BODY) {
        m_read_buffer[m_checked_idx + 1] = '\0';
        return LINE_OK;
    }
//    LOG_DEBUG("LINE_OPEN: read_ix_%d, checked_idx_%d", m_read_idx, m_checked_idx);
    return LINE_OPEN;
}

HttpRequest::HTTP_CODE HttpRequest::parse_request_line(const char *line) {
    // 正则表达式匹配
    const char req_pattern[] = "^([^ ]*) ([^ ]*) HTTP/([^ ]*)$";
    regex_t reg;
    regcomp(&reg, req_pattern, REG_EXTENDED);
    regmatch_t sub_match[5];
    if (regexec(&reg, line, 5, sub_match, 0) == 0) {
        m_method = std::string(line + sub_match[1].rm_so, line + sub_match[1].rm_eo);
        m_url = std::string(line + sub_match[2].rm_so, line + sub_match[2].rm_eo);
        m_version = std::string(line + sub_match[3].rm_so, line + sub_match[3].rm_eo);
        regfree(&reg);

        LOG_DEBUG("method: %s", m_method.c_str())
        LOG_DEBUG("url: %s", m_url.c_str())
        LOG_DEBUG("version: %s", m_version.c_str())

        process_path();
        return NO_REQUEST;
    } else {
        LOG_ERROR("Request Line Error!");
        return BAD_REQUEST;
    }
}

void HttpRequest::process_path() {
    if (m_url == "/") {
        m_path = "/index.html";
    } else if (DEFAULT_PAGES.count(m_url)){
        m_path = m_url + ".html";
    } else {
        m_path = m_url;
    }

    LOG_DEBUG("path: %s", m_path.c_str())
}

HttpRequest::HTTP_CODE HttpRequest::parse_headers(const char *line) {
    // 正则表达式匹配
    const char req_pattern[] = "^([^:]*): ?(.*)$";
    regex_t reg;
    regcomp(&reg, req_pattern, REG_EXTENDED);
    regmatch_t sub_match[5];
    int ret = regexec(&reg, line, 5, sub_match, 0);
    if (ret == 0) {
        std::string header_key(line + sub_match[1].rm_so, line + sub_match[1].rm_eo);
        std::string header_val(line + sub_match[2].rm_so, line + sub_match[2].rm_eo);
        m_header_tags[header_key] = header_val;
        regfree(&reg);
        return NO_REQUEST;
    } else if (ret == REG_NOMATCH && line[0] == '\0'){
        // 遇到空行后判断后面是否还有请求体
        if (m_header_tags.count("Content-Length") && m_header_tags["Content-Length"] != "0") {
            m_parse_state = PARSE_BODY;   // POST
            LOG_DEBUG("PARSE_HEADER -> PARSE_BODY");

            return NO_REQUEST;
        } else {
            return GET_REQUEST;
        }
    } else {
        return BAD_REQUEST;
    }
}

HttpRequest::HTTP_CODE HttpRequest::parse_post_body(const char *line) {
    LOG_DEBUG("parse_post_body()");
    m_body = std::string(line);
    if (m_method == "POST" && m_header_tags["Content-Type"] == "application/x-www-form-urlencoded"){
        // 提取请求体信息
        process_body_url();
        // 判断登录或注册请求
        bool is_login;
        if (m_url == "/register") {
            is_login = false;
        } else if (m_url == "/login") {
            is_login = true;
        } else {
            return BAD_REQUEST;
        }
        // 校验用户密码
        if (user_info_verify(m_body_kvs["username"], m_body_kvs["password"], is_login)) {
            m_path = "/welcome.html";
        } else {
            m_path = "/error.html";
        }
        return GET_REQUEST;

    } else {
        return BAD_REQUEST;
    }
}

void HttpRequest::process_body_url() {
    if (m_body.empty()) {
        return;
    }
    // 解码url
    std::string decoded_url;
    for (int i = 0; i < m_body.size(); ++i) {
        char ch = m_body[i];
        if (ch == '+') {
            decoded_url += ' ';
        } else if (ch == '%' && i+2 < m_body.size()) {
            std::string hex_str = m_body.substr(i+1, 2);
            long char_num = strtol(hex_str.c_str(), nullptr, 16);
            decoded_url += char(char_num);
            i += 2;
        } else {
            decoded_url += ch;
        }
    }
    LOG_DEBUG("Decoded Body: %s", decoded_url.c_str());

    // 提取k-v
    int i = 0, j = 0;
    std::string key, val;
    for (; i < decoded_url.size(); ++i) {
        char ch = decoded_url[i];
        if (ch == '=') {
            key = decoded_url.substr(j, i - j);
            j = i + 1;
        } else if (ch == '&'){
            val = decoded_url.substr(j, i - j);
            j = i + 1;
            m_body_kvs[key] = val;
            LOG_DEBUG("%s = %s", key.c_str(), val.c_str());
        }
    }
    if(m_body_kvs.count(key) == 0 && j < i) {
        val = decoded_url.substr(j, i - j);
        m_body_kvs[key] = val;
        LOG_DEBUG("%s = %s", key.c_str(), val.c_str());
    }
}


 HttpRequest::HTTP_CODE HttpRequest::process_request() {
    LINE_STATUS line_status = LINE_OK;
    HTTP_CODE http_code = NO_REQUEST;
    char *cur_line;

    while (m_parse_state != PARSE_FINISHED) {
        line_status = parse_line();
        if (line_status == LINE_BAD) {

            return BAD_REQUEST;
        } else if (line_status == LINE_OPEN){
            return NO_REQUEST;
        }

        // 遇到完整的行(LINE_OK)时进行解析并处理
        cur_line = m_read_buffer + m_line_idx;
        m_line_idx = m_checked_idx;
         LOG_INFO("cur_line: %s", cur_line);

        switch (m_parse_state) {
            case PARSE_REQUEST_LINE: {
                http_code = parse_request_line(cur_line);  //  NO_REQUEST || BAD_REQUEST
                if (http_code == BAD_REQUEST) {
                    return BAD_REQUEST;
                } else {
                    m_parse_state = PARSE_HEADER;
                    LOG_DEBUG("PARSE_REQUEST_LINE -> PARSE_HEADER");
                }
                break;
            }
            case PARSE_HEADER: {
                http_code = parse_headers(cur_line);     //  NO_REQUEST || BAD_REQUEST || GET_REQUEST
                if (http_code == GET_REQUEST) {
                    m_parse_state = PARSE_FINISHED;
                    LOG_DEBUG("PARSE_HEADER -> PARSE_FINISHED");
                } else if (http_code == BAD_REQUEST) {
                    return BAD_REQUEST;
                }
                break;
            }
            case PARSE_BODY: {
                http_code = parse_post_body(cur_line);    //  BAD_REQUEST || GET_REQUEST
                if (http_code == GET_REQUEST) {
                    m_parse_state = PARSE_FINISHED;
                    LOG_DEBUG("PARSE_BODY -> PARSE_FINISHED");
                } else {
                    return BAD_REQUEST;
                }
                break;
            }

            default: {
                return INTERNAL_ERROR;
            }
        }

    }

    return http_code;
}

bool HttpRequest::user_info_verify(const string &u_name, const string &u_passwd, bool is_login) {
    if (u_name.empty() || u_passwd.empty()) {
        return false;
    }

    LOG_INFO("Verifying the user with name:'%s' and pwd:'%s'.", u_name.c_str(), u_passwd.c_str());

    MySqlConn mysql_conn;
    assert(mysql_conn.get());

    // 查询用户及密码
//    MYSQL_FIELD *mysql_field = nullptr;
    MYSQL_RES *query_res = nullptr;
    char sql_cmd[256] = {0};
    snprintf(sql_cmd, 256, "SELECT username, password FROM users WHERE username='%s' LIMIT 1", u_name.c_str());

    LOG_DEBUG("MySql command: %s;", sql_cmd);

    if (mysql_query(mysql_conn.get(), sql_cmd) == 0) {
        query_res = mysql_store_result(mysql_conn.get());
//        unsigned int num_fields = mysql_num_fields(query_res);
//        mysql_field = mysql_fetch_field(query_res);
    } else {
        // 查询错误
        mysql_free_result(query_res);
        return false;
    }


    MYSQL_ROW row;
    while ((row = mysql_fetch_row(query_res)) != nullptr) {
        // 查询到用户名条目
        LOG_DEBUG("MYSQL ROW: %s %s", row[0], row[1]);

        string real_passwd(row[1]);
        if (is_login) {
            // 登录密码检验
            if (u_passwd == real_passwd) {
                LOG_DEBUG("Correctly verify the user named '%s'.", u_name.c_str());
                return true;
            } else {
                LOG_DEBUG("Invalid username or password.", u_name.c_str());
                return false;
            }
        }
        else {
            // 注册用户名存在
            LOG_DEBUG("The username: '%s' is unavailable!", u_name.c_str());
            return false;
        }
    }
    mysql_free_result(query_res);

    // 注册
    if (!is_login) {
        bzero(sql_cmd, 256);
        snprintf(sql_cmd, 256, "INSERT INTO users(username, password) VALUES('%s','%s')",
                 u_name.c_str(), u_passwd.c_str());
        LOG_DEBUG("MySql command: %s;", sql_cmd);

        if(mysql_query(mysql_conn.get(), sql_cmd)) {
            LOG_DEBUG("MySql INSERT error!");
            return false;
        }
        LOG_DEBUG("A new user named '%s' has been registered successfully.", u_name.c_str());
        return true;
    }

    return true;
}

bool HttpRequest::is_keepalive() const {
    if(m_header_tags.count("Connection") == 1) {
        return m_header_tags.find("Connection")->second == "keep-alive" && m_version == "1.1";
    }
    return false;
}










